feat(event-submission): URL-based artist tour import → auto-create artist-scoped pipeline (closes #320 DME side) - #322
Merged
Conversation
Contributor
Homeboy Results —
|
…tist-scoped pipeline Implements the DME side of extrachill-events#320. Adds a moderation queue (`artist_url_submissions` under $wpdb->base_prefix), four abilities, four REST routes, and an admin moderation screen. Abilities (`inc/Abilities/ArtistUrlImportAbilities.php`): - `data-machine-events/preview-artist-url` — non-destructive probe via UniversalWebScraper. Returns detected format, event count, preview list, and a suggested artist (term ID if a similar_text fuzzy match clears 85%, name otherwise). Logged-in users only. - `data-machine-events/submit-artist-url` — re-probes server-side (never trusts client detection) and inserts a row in `pending_review` (or `scraping_failed`). Logged-in users only. - `data-machine-events/approve-artist-url-submission` — admin only. Resolves the artist term (existing id, new name + wp_insert_term, or the submission's suggested term). Creates pipeline+flow via `datamachine/create-pipeline` + `datamachine/create-flow` mirroring the CityAbilities pattern, with universal_web_scraper handler. Triggers an immediate first run via `datamachine/run-flow`. - `data-machine-events/reject-artist-url-submission` — admin only. Marks the row rejected with an optional reason. SelectionMode alignment: the upsert step pins `taxonomy_artist_selection = (string) $artist_term_id` (PRE_SELECTED) and uses `SelectionMode::AI_DECIDES` constants by class reference for venue/location/festival, `SelectionMode::SKIP` for promoter, category, post_tag — no bare strings (#320 requirement). Dedupe: SHA-256 over a normalized URL (lowercased scheme/host, fragment stripped, trailing slash trimmed except root, default ports removed). UNIQUE KEY on url_hash; duplicate preview/submit returns `url_already_tracked`. REST: `POST /wp-json/datamachine/v1/artist-url/{preview,submit, {id}/approve,{id}/reject}`. Preview and submit reject direct-browser navigations (no XHR / JSON Accept header → 404), in the spirit of Admin moderation UI (`inc/Admin/ArtistUrlSubmissionsAdmin.php`): adds "Artist URL Imports" under Events. Tabs filter by status (pending review, approved, rejected, failed scrapes), inline forms for approve (artist term id OR new name + schedule) and reject (reason). Forms post to admin-post.php with nonces and delegate to the abilities. Tests (`tests/Unit/ArtistUrlSubmissionsTableTest.php`, `tests/Unit/ArtistUrlImportAbilitiesTest.php`): URL normalization, url_hash dedupe, table CRUD, preview/submit rejection paths, `artist_required` error from approve when no artist input is provided, reject sets status + reason. Approve's pipeline-creation integration is covered by curl verification against a live install (matrix in the PR body) — recreating Data Machine core abilities in isolated unit tests would be fake coverage. The extrachill-events side (form change consuming preview + submit abilities) ships separately once this merges.
chubes4
force-pushed
the
feat-320-artist-url-import
branch
from
May 27, 2026 02:23
96165f8 to
6f87b1f
Compare
chubes4
added a commit
to Extra-Chill/extrachill-events
that referenced
this pull request
May 27, 2026
…EC-events side) (#118) Adds a logged-in-user-only URL import field at the top of the event-submission block. When the user pastes a tour page URL and tabs away (or clicks 'Try URL' / hits Enter), the block calls the `data-machine-events/preview-artist-url` REST ability and renders a confirmation panel showing the events count, suggested artist, and the first few extracted events. On confirm, the block calls `data-machine-events/submit-artist-url` to queue the URL for admin moderation. Behavior: - On preview success: manual form is hidden, confirmation panel appears with 'Found N events from [artist]. Submit for review?'. - On 'url_already_tracked': clear message + reveal manual form so the user can submit a single-event instead. - On 'no_events_found' or any other error: error message + reveal manual form. - Cancel button restores the idle state and shows the manual form. - 8s client-side timeout on probe; AbortController-based. - Anonymous users never see the URL block (the DME ability rejects them anyway). The existing manual single-event submission path is unchanged — when the user doesn't touch the URL field, the form behaves byte-identically to before. No admin-ajax, no jQuery; uses fetch + REST nonce. REST endpoints + nonce are passed via data-* attributes on the container so view.js stays static. Depends on Extra-Chill/data-machine-events#322 (must be merged first for the REST endpoints + abilities to exist).
This was referenced May 27, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the DME side of #320. The extrachill-events form change consuming the new abilities ships in a follow-up PR against
extrachill-eventsafter this merges.Summary
Adds URL-based artist tour import: a logged-in user pastes a tour page URL (e.g.
https://theokatzman.com/tour), the system probes it viaUniversalWebScraper, and queues it for admin moderation. On approval, an admin picks/creates the artist term, and DME programmatically creates an artist-scoped Data Machine pipeline that polls the URL on a schedule — every artist with a parseable site becomes self-onboarding.Phases delivered
A. Table (
inc/Core/ArtistUrlSubmissionsTable.php){base_prefix}artist_url_submissionsinstalled viadbDeltaonplugins_loaded(version-gated). UNIQUE KEY onurl_hash(SHA-256 of normalized URL) enforces dedupe. Status enum:pending_review|approved|rejected|scraping_failed. Lives under$wpdb->base_prefixso the moderation queue is shared across the multisite as required.B. Abilities (
inc/Abilities/ArtistUrlImportAbilities.php)Four abilities, all self-registering on
wp_abilities_api_init:data-machine-events/preview-artist-url{ detected_format, events_found, events_preview, suggested_artist_name, suggested_artist_term_id, source_metadata }data-machine-events/submit-artist-urldata-machine-events/approve-artist-url-submissionmanage_optionsdatamachine/create-pipeline+datamachine/create-flowmirroringCityAbilities, triggers first run viadatamachine/run-flowdata-machine-events/reject-artist-url-submissionmanage_optionsSelectionMode alignment (#320 hard requirement) — every taxonomy mode in the upsert step config uses class constants by reference, never bare strings:
Artist name auto-detection runs inside
preview-artist-url:Performer/MusicGroupon the first extracted event.<meta property="og:title">/<title>/ first<h1>with site-name suffixes stripped (Tour,Events,Shows,Live, etc.).www.→ first label → Title Case.The detected name is then fuzzy-matched against existing
artistterms viasimilar_text(); if the highest match clears 85% the term ID is returned, otherwisenulland the admin types a name during approval.Dedupe —
normalize_url()lowercases scheme + host, strips fragment, trims trailing slash (except root), drops default ports.url_hash = sha256(normalized). The UNIQUE KEY on the table makes duplicate preview/submit returnurl_already_trackedwith the existing submission's status.CityAbilities pattern reuse — pipeline scaffold (
event_import → ai → update), AI step configured with an artist-scoped system prompt, belt-and-bracespatchFlowSteps()writes handler slugs/configs directly to the flow_config JSON to harden againstcreate-flow's timing quirks (same shape asCityAbilities::patchFlowSteps).C. REST routes (
inc/Api/Controllers/ArtistUrlImport.php+inc/Api/Routes.php)/wp-json/datamachine/v1/artist-url/preview/wp-json/datamachine/v1/artist-url/submit/wp-json/datamachine/v1/artist-url/{id}/approvemanage_options/wp-json/datamachine/v1/artist-url/{id}/rejectmanage_optionsPreview + submit reject direct-browser navigations (no
X-Requested-With: XMLHttpRequest/application/jsonAccept header → 404) in the spirit of #297 hardening. No sharedBrowserNavigationGuardclass exists in the tree yet, so the guard is inline; if/when one lands, these endpoints can be migrated.D. Admin moderation UI (
inc/Admin/ArtistUrlSubmissionsAdmin.php)New submenu under Events → Artist URL Imports. Status tabs: Pending review (default) | Approved | Rejected | Failed scrapes. Per-row inline forms for approve (artist term ID or new name + schedule interval) and reject (optional reason). Submits post to
admin-post.phpwith nonces and delegate to the abilities.E. Tests
tests/Unit/ArtistUrlSubmissionsTableTest.php— normalization (scheme casing, fragment, trailing slash, default ports, query preservation),url_hashdeterminism, dedupe via UNIQUE constraint, CRUD round-trip, counts_by_status.tests/Unit/ArtistUrlImportAbilitiesTest.php— preview rejects empty/non-http/duplicate URLs, returnsno_events_foundfor an empty-page mock viapre_http_request, submit recordsscraping_failedrow when probe returns nothing, submit rejects duplicate URLs, approve returnsartist_requiredwhen no artist provided and submission has no suggested term, approve rejects non-pending submissions, reject sets status + reason + reviewer.Tests match the
WP_UnitTestCase/DataMachineEvents\Tests\Unitshape of the existing suite (e.g.UpcomingCountAbilitiesTest,EventScraperTestAbilityTest). CI runshomeboy audit; the audit summary has no new outliers introduced by these files.What NOT done (per issue + prompt)
ai_decidesstrings —SelectionModeconstants only.datamachine/create-flowonly.Curl matrix for the four abilities (verified against live install)
Follow-up
The extrachill-events form change (URL field at the top of the event-submission block, JS probe via preview ability, confirm + submit) ships in a separate PR against
extrachill-eventsonce this merges. Filing the issue close on the EC-events PR.mention <@532385681268408341> when ready for review.